﻿//Copyright (C) <2013>  <jonathan cleeve norton> All Rights Reserved 
//Contact jon.norton@fin-plus.co.uk website <http://www.fin-plus.co.uk/>
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Xml;
using FinPlusAssembler;
using FinPlusInterfaces;

namespace FinPlusBaseCore
{
    public class Pods : ConcurrentDictionary<int, IPod>, IPods
    {
        public new IPod this[int Id] { get { return base[Id]; } set { Add(value, false, false); } }
        public EventHandler<BeanEventArgs> PodUpdate { get; set; }
        public string Id { get; set;}

        //construct
        public Pods() {Id = Guids.New(); }

        public Pods(string xmlPathOrString) : this(xmlPathOrString, "root/current", "") { }

        public Pods(string xmlPathOrString, string xmlNode) : this(xmlPathOrString, xmlNode, "") { }

        public Pods(string xmlPathOrString, string xmlNode, string replaceValue) { this.FromXml(xmlPathOrString, xmlNode, replaceValue); }

        //public
        public IPods Clone()
        {
            var clone = new Pods();

            lock (this)
                foreach (var pod in this.Values)
                    clone.Add(pod.Clone(pod.Id));

            return clone;
        }

        public void Refresh()
        {
            foreach (var pod in this.Values)
                pod.Refresh();
        }

        public IPod Add(IPod value, bool nextId = true, bool setId = true)
        {
            value.LinkedId = Id;
            value.BeanUpdate += Pod_Update;

            if (nextId)
              value.Id = value.Id <= 0 ? NextKey() : value.Id;
            
            if (setId)
                value.Get(PodField.Id.ToString(), value.Id).Update(value.Id);

            base.TryAdd(value.Id, value);

            return value;
        }

        public IPod Get(string id) { return this.First("Name='Id' And Value='" + id + "'"); }

        public void Get<T>(string name, T defaultValue, PropertyChangedEventHandler changeEventHander = null)
        {
            foreach (var pod in Values)
                pod.Get(name, defaultValue, changeEventHander);
        }

        public void Set<T>(string name, string value, T defaultValue)
        {
            foreach (var pod in Values)
                pod.Get(name, defaultValue).Update(value);
        }

        public void Update(IPod value, ChangeType changeType)
        {
            value.Id = (int)value.Get(PodField.Id.ToString()).ToString().IntParse(-1);

            IPod podUpdate = null;

            if (changeType == ChangeType.Removed && TryRemove(value.Id, out podUpdate) && PodUpdate != null)
                PodUpdate.Invoke((object)value, new BeanEventArgs(null, changeType));
            else if (changeType == ChangeType.New && Add(value) != null && PodUpdate != null)
                PodUpdate.Invoke((object)value, new BeanEventArgs(null, changeType));
            else if (changeType == ChangeType.Altered && TryGetValue(value.Id, out podUpdate))
                podUpdate.Update(value);
        }

        public void Remove(string expression)
        {
            try
            {
                lock (this)
                {
                    IPod f = null;
                    var podIds = (from pod in this.Values where pod.IsMatch(expression) == false select pod.Id).ToArray();
                    if (podIds.Any())
                        foreach (var podId in podIds)
                            this.TryRemove(podId, out f);
                }
            }
            catch
            {
                
            }
        }

        public new bool TryRemove(int id, out IPod value)
        {
            return base.TryRemove(id, out value);
        }

        public IPod First(string expression)
        {
            try
            {
                var res = this.Values.Where(f => f.IsMatch(expression));
                if(res.Count() <1 ) 
                    return null;
                return res.First();
            }
            catch
            {
                return null;
            }
        }

        public IDictionary<string, IPod> Where(string expression, string key = "Id")
        {
            try
            {
                return this.Values.Where(f => f.IsMatch(expression)).ToDictionary(k => k[key].ToString(), f => f);
            }
            catch
            {
                return null;
            }
        }

        public XmlDocument ToXmlDoc(string nodes = "root/current") { return this.ToXml(nodes); }

        public override string ToString()
        {
            return this.Display(BeanField.Value, null, null);
        }

        public IPod[] ToArray(string name, string match)
        {
            return Values.Where(f => f[name].ToString().Equals(match)).ToArray<IPod>();
        }

        public IList<IPod> ToList(string name, string match, char delimitor = '\0')
        {
           if(delimitor.Equals('\0'))
               return Values.Where(f => f.Get(name).ToString().Equals(match)).ToList();

            return Values.Where(f => f[name].ToString().Split(delimitor).ToList().Contains(match)).ToList();
        }

        public IList<string> ToList(string name)
        {
            return Values.Where(f => f.Name.Equals(name)).Select(f => f[name].ToString()).ToList<string>();
        }

        public IDictionary<string, string> ToDictionary(string key, string value)
        {
            return Values.ToDictionary(f => f[key].ToString(), f => f[value].ToString());
        }

        public int NextKey() { return this.Count() < 1 ? 0 : this.Max(kvp => kvp.Key) + 1; }

        public void FromXml(string xmlPathOrString, string xmlNodes, string replaceValue)
        {
            Id = Guids.New();
            var replace = replaceValue.ToDict(true, ';', ',', string.Empty);

            var doc = new XmlDocument();
            if (xmlPathOrString.Contains('<'))
                doc.Load(new StringReader(xmlPathOrString));
            else
                doc.Load(xmlPathOrString);

            var id = 1;
            foreach (XmlElement xmlPods in doc.SelectNodes(xmlNodes))
                AddXml(ref id, xmlPods, replace);
        }

        public void AddXml(ref int id, XmlElement xml, IDictionary<string, string> replace)
        {
            foreach (XmlElement xmlPod in xml)
            {
                var pod = new Pod(id, xmlPod, replace);
                pod.LinkedId = Id;
                TryAdd(id, pod);
                id++;

                foreach (XmlElement xmlChildPods in xmlPod)
                    if (xmlChildPods.Name.Equals("Dock"))
                        AddXml(ref id, xmlChildPods, replace);
            }
        }

        public XmlDocument ToXml(string nodes)
        {
            var xmlDoc = new XmlDocument();
            try
            {
                XmlNode xmlNode = null, xmlNodePrev = null;

                foreach (var node in nodes.Trim('/').Split('/'))
                {
                    xmlNode = xmlDoc.CreateNode(XmlNodeType.Element, node, null);
                    if (xmlNodePrev == null)
                        xmlDoc.AppendChild(xmlNode);
                    else
                        xmlNodePrev.AppendChild(xmlNode);
                    xmlNodePrev = xmlNode;
                }

                foreach (var pod in this.Values)
                    pod.ToXml(xmlDoc, nodes);
            }
            catch
            {
                throw;
            }
            return xmlDoc;
        }

        //private
        private void Add(IPods value, bool nextId = true, bool setId = true)
        {
            foreach (var pod in value.Values)
                this.Add(pod, nextId, setId);
        }

         //events
        private void Pod_Update(object s, BeanEventArgs a)
        {
            if (PodUpdate != null)
                PodUpdate.Invoke(s, a);
        }
    }
}




































































































































//Copyright2013<jnrton>AllRightsReserved 
//Contact j.nrton@finpus